package com.techshinogongjian.fp.comm;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.fingerUsb.FingerResult;
import com.techshinogongjian.fp.util.FpCoder;
import com.techshinogongjian.fp.util.FpConst;
import com.techshinogongjian.fp.util.FpImage;
import com.techshinogongjian.fp.util.FpProtocol;
import com.techshinogongjian.fp.util.Logs;

/**
 * laserX协议
 */
public class LaserXComm extends IComm {

	private static final String TAG = LaserXComm.class.getSimpleName();

	InputStream mInputStream;
	OutputStream mOutputStream;
	private boolean bIsOpened = false;

	// private int nCmdDataFormat = FpConst.LSR_SPT;
	private int nCmdDataFormat = FpConst.LSR_BIN;

	private int nCmdHeadChar = FpConst.COM_HDR;
	private FpProtocol nCmdProtocol = FpProtocol.CXY_LASERX;

	// 命令行发送缓冲区
	private byte[] bSendBuf = null;
	private int nSendDatLen = 0;
	// 接收缓冲区
	private byte[] bRecvBuf = null;
	private int nRecvDatLen = 0;
	private int nCmdAckVal = 0;

	private int nTimeoutDoFeature = 18000; // 取验证指纹的超时限定
	private int nTimeoutDoComm = 5000; // 单个指令通信超时限定
	private int nTimeoutDoTemplt = 32000; // 去注册模版的超时限定

	private int nLogLevel = 0;

	private String sCurrHexCmdId = "";

	private int mWidth;
	private int mHeight;
	private byte[] mImgByte;
	
	private boolean isInitOk = false;
	private MsgCallback mCallback;

	public LaserXComm(InputStream in, OutputStream out) {
		mInputStream = in;
		mOutputStream = out;

		bSendBuf = new byte[1024];
		bRecvBuf = new byte[1024];
	}

	public int getRecvDatLen() {
		return nRecvDatLen;
	}

	public byte[] getbRecvBuf() {
		return bRecvBuf;
	}

	private boolean isWell(int nRet) {
		if (nRet >= 0)
			return true;
		return false;
	}
	
	public void setInitOk(boolean isInitOk) {
		this.isInitOk = isInitOk;
	}
	
	public boolean isInitOk() {
		return isInitOk;
	}

	/**
	 * 获取设备内核信息
	 */
	public int lxCmdDevInfo() {
		int nRet;
		bSendBuf[0] = 0x00;
		nSendDatLen = 1;
		nRet = doCommand(FpConst.LSR_INFO);
		return nRet;
	}

	/**
	 * 变更设备波特率
	 *
	 */
	public int lxCmdBaudChange(int nBaudRate) {
		int nRet;
		if ((nBaudRate < 0) || (nBaudRate > 7)) {
			return FpConst.TCY_PARA;
		}
		bSendBuf[0] = (byte) (nBaudRate & 0x000000FF);
		nSendDatLen = 1;
		nRet = doCommand(FpConst.LSR_BAUD);
		return nRet;
	}

	/**
	 * 设备内超级注册，自动完成3次抓拍，并合成 可选是否附加第4次测试
	 * 
	 * @param nAttach4Test
	 * @return
	 */
	public int lxCmdRegistInDev(int nAttach4Test) {
		int nRet;
		bSendBuf[0] = (byte) (nAttach4Test & 0x000000FF);
		nSendDatLen = 1;
		nRet = doCommand(FpConst.LSR_REGI, nTimeoutDoTemplt);
		return nRet;
	}

	/**
	 * 设备内1：N，可以用过提取特征
	 * 
	 * @return
	 */
	public int lxCmdVerifyInDev(byte[] bFeatOut) {
		int nRet = FpConst.TCY_FAIL;
		bSendBuf[0] = 0x02;
		nSendDatLen = 1;
		nRet = doCommand(FpConst.LSR_VERI);
		if (nRet == FpConst.TCY_SUCC) {
			if (nRecvDatLen > 2) {
				if (bFeatOut != null) {
					System.arraycopy(bRecvBuf, 2, bFeatOut, 0, nRecvDatLen - 2);
				}
				nRet = nRecvDatLen - 2;
			}
		}
		return nRet;
	}

	/**
	 * 
	 */

	/**
	 * 提取0x64指令抓拍到的图像对应的特征值 如果返回 LSE_NOFP 则标识，则需要继续调用0x64抓拍
	 * 
	 * @return
	 */
	public int lxCmdExtractFromCurrImg() {
		int nRet;
		bSendBuf[0] = 0x00;
		bSendBuf[1] = 0x01;
		nSendDatLen = 2;
		nRet = doCommand(FpConst.LSR_MNUT);
		return nRet;
	}

	/**
	 * 抓拍一帧指纹图像，并切检测是否有指纹按下 如果返回LSE_NOFP 则无手指，需要继续抓拍
	 * 
	 */
	public int lxCmdCapture(int nRdImg) {
		int nRet;
		bSendBuf[0] = 0x00;
		bSendBuf[1] = 0x01;
		bSendBuf[2] = 0x0c;
		bSendBuf[3] = 0x00;
		nSendDatLen = 4;
		nRet = doCommand(FpConst.LSR_CAPT);

		isCommTimeout(30000);

		if (nRet < 0) {
			return nRet;
		}

		Logs.i(TAG, "doRecvCmd...");
		byte[] bRecvHead = new byte[32];
		int nDataLen = 0;

		nCmdAckVal = FpConst.LSE_FAIL;
		nRecvDatLen = 0;

		if (isOpened() == false) {
			return FpConst.TCY_NLNK;
		}

		// 指令头
		nRet = doRecvCmdHDR();
		if (nRet < FpConst.TCY_SUCC) {
			Logs.i(TAG, "doRecvCmd  Fail. nRet =" + nRet);
			return nRet;
		} else {
			bRecvHead[0] = (FpConst.COM_HDR & 0x000000FF);
		}
		// 指令格式
		nRet = doRecvBin(bRecvHead, 1, 1, FpConst.LSR_BIN);
		if (nRet < FpConst.TCY_SUCC) {
			Logs.e(TAG, "doRecvCmd FMT Fail. nRet =" + nRet);
			return nRet;
		} else {
			Logs.i(TAG, "doRecvCmd FMT OK. nRet =" + (int) bRecvHead[1]);
		}
		// 指令头其余部分
		nRet = doRecvBin(bRecvHead, 2, 6, nCmdDataFormat);
		if (nRet < FpConst.TCY_SUCC) {
			Logs.i(TAG, "doRecvCmd CMD/ACK/LEN Fail. nRet =" + nRet);
			return nRet;
		} else {
			// 已经都是Bin格式了
			Logs.i(TAG, "doRecvCmd. nRet =" + nRet);
			HexOut(TAG, bRecvHead, 0, 8);
			// 获得长度
			nDataLen = ((bRecvHead[4] << 24) & 0xFF000000)
					| ((bRecvHead[5] << 16) & 0x00FF0000)
					| ((bRecvHead[6] << 8) & 0x0000FF00)
					| ((bRecvHead[7]) & 0x000000FF);
			// 指令状态
			nCmdAckVal = bRecvHead[3];
			Logs.i(TAG, "doRecvCmd parse nDataLen =" + nDataLen);
		}
		// 接收BODY区
		byte[] bDataChk = new byte[nDataLen + 1];
		nRet = doRecvBin(bDataChk, 0, nDataLen + 1, nCmdDataFormat);

		if (nRet < FpConst.TCY_SUCC) {
			Logs.i(TAG, "doRecvCmd Data/CHK Fail. nRet =" + nRet);
			return nRet;
		} else {
			Logs.i(TAG, "doRecvCmd Data/CHK OK. =" + nRet);
		}
		// 拼组整体响应报文区
		int nTotalLen = 1 + 1 + 1 + 1 + 4 + nDataLen + 1;
		Logs.i(TAG, "doRecvCmd sum nTotalLen =" + nTotalLen);
		byte[] bRecvALL = new byte[nTotalLen];
		System.arraycopy(bRecvHead, 0, bRecvALL, 0, 8);
		Logs.i(TAG, "bRecvHead.length:" + bRecvHead.length
				+ " bRecvALL.length:" + bRecvALL.length);
		System.arraycopy(bDataChk, 0, bRecvALL, 8, nDataLen + 1);

		int nChk = FpCoder.XorChk(bRecvALL, 1, (3 + 4 + nDataLen));
		Logs.i(TAG, "chk & 0x0000fff:" + nChk + " "
				+ ((byte) (nChk & 0x000000FF)));
		Logs.i(TAG, "bRecvALL[nTotalLen - 1]:" + bRecvALL[nTotalLen - 1]);
		if (bRecvALL[nTotalLen - 1] != (byte) (nChk & 0x000000FF)) {
			// HexOut("doRecvCmd CHK Fail. BufDebug:", bRecvALL, 0, nTotalLen);
			return FpConst.TCY_ERRO;
		} else {
			Logs.i(TAG, "doRecvCmd sum CHK OK");
			// 终于执行完了
			if (nDataLen > 0) {
				// System.arraycopy(bDataChk, 0, bRecvBuf, 0, nDataLen);
				// nRecvDatLen = nDataLen;
				HexOut(TAG, bDataChk, 0, 28);
				byte[] bW = new byte[2];
				byte[] bH = new byte[2];
				byte[] bSize = new byte[4];

				System.arraycopy(bDataChk, 6, bW, 0, bW.length);
				System.arraycopy(bDataChk, 8, bH, 0, bH.length);
				System.arraycopy(bDataChk, 20, bSize, 0, bSize.length);

				mWidth = FpCoder.twoByteToInt(bW);
				mHeight = FpCoder.twoByteToInt(bH);
				int size = FpCoder.fourByteToInt(bSize);

				Logs.d(TAG, "wdith:" + mWidth + " height:" + mHeight + " size:"
						+ size);

				mImgByte = new byte[size];
				System.arraycopy(bDataChk, 28, mImgByte, 0, size);
			}
		}
		nRet = CnvtErrLaserX(nCmdAckVal);
		Logs.i(TAG, "doRecvCmd Ack=" + nCmdAckVal + ", Cvt to nRet=" + nRet);

		return nRet;
	}

	/**
	 * 读取Flash的指令
	 * 
	 * @param nSecAndOffSet
	 * @param nRdLen
	 * @return
	 */
	public int lxCmdReadFlash(int nSecAndOffSet, int nRdLen) {
		int nRet;
		int nSection, nOffSet;
		nSection = nSecAndOffSet / 0x10000;
		nOffSet = nSecAndOffSet % 0x10000;

		bSendBuf[0] = 0x00;
		bSendBuf[1] = (byte) (nSection & 0x000000FF);
		bSendBuf[2] = (byte) ((nOffSet >> 8) & 0x000000FF);
		bSendBuf[3] = (byte) (nOffSet & 0x000000FF);
		byte[] rdLens = FpCoder.Int2Byte(nRdLen);
		Logs.i(TAG, "rdLens=" + rdLens.length);
		HexOut("rdLens=", rdLens, 0, rdLens.length);
		System.arraycopy(rdLens, 0, bSendBuf, 4, 4);
		HexOut("bSendBuf=", bSendBuf, 0, 8);
		nSendDatLen = 8;
		nRet = doCommand(FpConst.LSR_FLSR);
		return nRet;
	}

	/**
	 * 设备参数控制
	 * 
	 * @param nRdWrt
	 * @param nDitoType
	 * @param nParamVal
	 * @return
	 */
	public int lxCmdDito(int nRdWrt, int nDitoType, int nParamVal) {
		int nRet;
		bSendBuf[0] = (byte) (nRdWrt & 0x000000FF);
		bSendBuf[1] = (byte) (nDitoType & 0x000000FF);
		bSendBuf[2] = 0;
		bSendBuf[3] = 0;
		nSendDatLen = 4;
		if (nRdWrt == 1) {
			if (nDitoType == 8) {
				bSendBuf[4] = (byte) (nParamVal & 0x000000FF);
				nSendDatLen += 1;
			}
		}
		nRet = doCommand(FpConst.LSR_DITO);
		return nRet;
	}

	public int lxCmdDevLink(byte[] bDevInfo) {
		int nRet;
		nSendDatLen = 0;
		nRet = doCommand(FpConst.LSR_LINK);
		if (nRet == FpConst.TCY_SUCC) {
			/*
			 * DATA[0]＝系列号， DATA[1]＝子功能号； DATA[2]＝版本号 H， DATA[3]＝版本号 L；
			 * DATA[4]＝CPU 标识， DATA[5]＝Sensor 标识； DATA[6]＝图半宽度， DATA[7]＝图半高度；
			 */
			HexOut("DevLink:", bRecvBuf, 0, 8);
			System.arraycopy(bDevInfo, 0, bRecvBuf, 0, 8);
		}
		return nRet;
	}

	public int lxCmdSubDev(int nParamId, int nVal) {
		int nRet;
		bSendBuf[0] = (byte) (nParamId & 0x000000FF);
		bSendBuf[1] = (byte) (nVal & 0x000000FF);
		nSendDatLen = 2;
		nRet = doCommand(FpConst.LSR_CTRL);
		return nRet;
	}

	public int lxCmdConfig(int nParamId, int nVal) {
		int nRet;
		bSendBuf[0] = (byte) (nParamId & 0x000000FF);
		bSendBuf[1] = (byte) (0x01);
		bSendBuf[2] = (byte) (nVal & 0x000000FF);
		nSendDatLen = 3;
		nRet = doCommand(FpConst.LSR_SCFG);
		return nRet;
	}

	/**
	 * 发送指令
	 * 
	 * @param bCmdBytes
	 * @param nCmdSendLen
	 * @return
	 */
	private int doSendCmd(byte[] bCmdBytes, int nCmdSendLen) {
		HexOut("doSendCmd", bCmdBytes, 0, nCmdSendLen);
		int nRet = 0;
		if (isOpened() == false) {
			return FpConst.TCY_NLNK;
		}
		nRet = write(bCmdBytes, 0, nCmdSendLen);
		if (nRet >= 0) {
			flush();
		}
		return nRet;
	}

	private int doRecvCmdHDR() {
		int nRet = 0;
		byte[] bRecvHead = new byte[32];
		int nRdLen = 0;
		boolean bStart = false;

		if (isOpened() == false) {
			return FpConst.TCY_NLNK;
		}
		bRecvHead[0] = 0x00;
		bRecvHead[1] = 0x00;
		// 接收请求头
		while (true) {
			nRet = read(bRecvHead, nRdLen, 1);
			if (nRet <= 0) {
				Logs.i(TAG, "doRecvCmdHDR.mComm.read nRet=" + nRet);
				if (nRet == 0) {
					nRet = FpConst.TCY_ERRO;
					Logs.i(TAG, "doRecvCmdHDR.mComm.read Cvt2 nRet=" + nRet);
				}
				break;
			} else {
				if (bRecvHead[0] == (FpConst.COM_HDR & 0x000000FF)) {
					nRet = 0;
					break;
				} else {
					Logs.i(TAG, "doRecvCmdHDR.drop data.");
				}
			}
		}
		// 正常收到头字节
		return nRet;
	}

	private int doRecvBin(byte[] bCmdRecvBin, int nStartPos, int nBinLen,
			int nCmdFmt) {
		int nRet = 0;
		int nRdLen = 0;
		int nWantLen = nBinLen;
		byte[] bRecvtmp = null;

		if (isOpened() == false) {
			return FpConst.TCY_NLNK;
		}
		if ((nCmdFmt == FpConst.LSR_SPT) || (nCmdFmt == FpConst.LSB_SPT)) {
			nWantLen = nBinLen * 2;
		}
		bRecvtmp = new byte[nWantLen];
		nRet = read(bRecvtmp, 0, nWantLen);
		
		Logs.i(TAG,"读取图片数据完成：" + nRet);
		if (nRet <= 0) {
			Logs.i(TAG, "doRecvBin.mComm.read nRet=" + nRet);
			return nRet;
		} else {
			nRdLen = nRet;
			if (nRdLen > 0)
				// HexOut("doRecvBin Debug:", bRecvtmp, 0, nRdLen);
				if (nRdLen < nWantLen) {
					Logs.i(TAG, "doRecvBin Response Fail. nRdLen =" + nRdLen);
					return FpConst.TCY_ERRO;
				}
		}
		// 解析指令
		if ((nCmdFmt == FpConst.LSR_SPT) || (nCmdFmt == FpConst.LSB_SPT)) {
			byte[] bTmpBin = FpCoder.DecodeX30(bRecvtmp, 0, nWantLen);
			if (bTmpBin.length != nBinLen) {
				if (bTmpBin.length > 0)
					// HexOut("doRecvBin Debug: DecodeX30", bTmpBin, 0,
					// bTmpBin.length);
					return FpConst.TCY_ERRO;
			} else {
				System.arraycopy(bTmpBin, 0, bCmdRecvBin, nStartPos,
						bTmpBin.length);
				Logs.i(TAG, "doRecvBin Response Fail. nRdLen =" + nRdLen);
			}
		} else {
			System.arraycopy(bRecvtmp, 0, bCmdRecvBin, nStartPos, nRdLen);
			nRet = nRdLen;
		}
		return nRet;
	}

	/**
	 * 接收指令响应包 LaserX协议
	 * 
	 * @param bCmdBytes
	 * @return
	 */
	private int doRecvCmd() {
		Logs.i(TAG, "doRecvCmd...");
		int nRet = 0;
		byte[] bRecvHead = new byte[32];
		int nDataLen = 0;

		nCmdAckVal = FpConst.LSE_FAIL;
		nRecvDatLen = 0;

		if (isOpened() == false) {
			return FpConst.TCY_NLNK;
		}

		// 指令头
		nRet = doRecvCmdHDR();
		if (nRet < FpConst.TCY_SUCC) {
			Logs.i(TAG, "doRecvCmd  Fail. nRet =" + nRet);
			return nRet;
		} else {
			bRecvHead[0] = (FpConst.COM_HDR & 0x000000FF);
		}
		// 指令格式
		nRet = doRecvBin(bRecvHead, 1, 1, FpConst.LSR_BIN);
		if (nRet < FpConst.TCY_SUCC) {
			Logs.e(TAG, "doRecvCmd FMT Fail. nRet =" + nRet);
			return nRet;
		} else {
			Logs.i(TAG, "doRecvCmd FMT OK. nRet =" + (int) bRecvHead[1]);
		}
		// 指令头其余部分
		nRet = doRecvBin(bRecvHead, 2, 6, nCmdDataFormat);
		if (nRet < FpConst.TCY_SUCC) {
			Logs.i(TAG, "doRecvCmd CMD/ACK/LEN Fail. nRet =" + nRet);
			return nRet;
		} else {
			// 已经都是Bin格式了
			Logs.i(TAG, "doRecvCmd. nRet =" + nRet);
			HexOut(TAG, bRecvHead, 0, 8);
			// 获得长度
			nDataLen = ((bRecvHead[4] << 24) & 0xFF000000)
					| ((bRecvHead[5] << 16) & 0x00FF0000)
					| ((bRecvHead[6] << 8) & 0x0000FF00)
					| ((bRecvHead[7]) & 0x000000FF);
			// 指令状态
			nCmdAckVal = bRecvHead[3];
			Logs.i(TAG, "doRecvCmd parse nDataLen =" + nDataLen);
		}
		// 接收BODY区
		byte[] bDataChk = new byte[nDataLen + 1];
		nRet = doRecvBin(bDataChk, 0, nDataLen + 1, nCmdDataFormat);
		if (nRet < FpConst.TCY_SUCC) {
			Logs.i(TAG, "doRecvCmd Data/CHK Fail. nRet =" + nRet);
			return nRet;
		} else {
			Logs.i(TAG, "doRecvCmd Data/CHK OK. =" + nRet);
		}
		// 拼组整体响应报文区
		int nTotalLen = 1 + 1 + 1 + 1 + 4 + nDataLen + 1;
		Logs.i(TAG, "doRecvCmd sum nTotalLen =" + nTotalLen);
		byte[] bRecvALL = new byte[nTotalLen];
		System.arraycopy(bRecvHead, 0, bRecvALL, 0, 8);
		Logs.i(TAG, "bRecvHead.length:" + bRecvHead.length
				+ " bRecvALL.length:" + bRecvALL.length);
		System.arraycopy(bDataChk, 0, bRecvALL, 8, nDataLen + 1);
		// Logs.i(TAG,"bDataChk.length:" + bDataChk.length + " bDataChk:" +
		// bDataChk[8]
		// + " bRecvALL.length:" + bRecvALL.length);
		HexOut("ALL:", bRecvALL, 0, nTotalLen);
		// 核验校验值
		int nChk = FpCoder.XorChk(bRecvALL, 1, (3 + 4 + nDataLen));
		Logs.i(TAG, "chk & 0x0000fff:" + nChk + " "
				+ ((byte) (nChk & 0x000000FF)));
		Logs.i(TAG, "bRecvALL[nTotalLen - 1]:" + bRecvALL[nTotalLen - 1]);
		if (bRecvALL[nTotalLen - 1] != (byte) (nChk & 0x000000FF)) {
			HexOut("doRecvCmd CHK Fail. BufDebug:", bRecvALL, 0, nTotalLen);
			return FpConst.TCY_ERRO;
		} else {
			Logs.i(TAG, "doRecvCmd sum CHK OK");
			// 终于执行完了
			if (nDataLen > 0) {
				System.arraycopy(bDataChk, 0, bRecvBuf, 0, nDataLen);
				nRecvDatLen = nDataLen;
			}
		}
		nRet = CnvtErrLaserX(nCmdAckVal);
		Logs.i(TAG, "doRecvCmd Ack=" + nCmdAckVal + ", Cvt to nRet=" + nRet);
		return nRet;
	}

	/*
	 * 执行 LaserX协议命令
	 * 
	 * 指令接口与外部交互均已Bin格式进行， 命令执行过程可以选用Split x30格式
	 */
	private int doCmdLaserX(int nCmdId) {
		int nRet = 0;

		// HDR1 + FMT1 + CMD1 + LEN4 + DATA + CHK1
		int nSendLen = 1 + 1 + 1 + 4 + nSendDatLen + 1;
		byte[] bSend = new byte[nSendLen];

		// 拼组Bin格式请求命令
		bSend[0] = (byte) (nCmdHeadChar & 0x000000FF);
		bSend[1] = (byte) (nCmdDataFormat & 0x000000FF);
		bSend[2] = (byte) (nCmdId & 0x000000FF);
		byte[] bLen = FpCoder.Int2Byte(nSendDatLen);
		System.arraycopy(bLen, 0, bSend, 3, 4);
		if (nSendDatLen > 0) {
			System.arraycopy(bSendBuf, 0, bSend, 7, nSendDatLen);
		}
		int nChk = FpCoder.XorChk(bSend, 1, nSendDatLen + 6);
		bSend[nSendLen - 1] = (byte) (nChk & 0x000000FF);

		// 改为x30拆分格式
		if (FpConst.LSR_SPT == nCmdDataFormat) {
			nSendLen = 1 + (1 + 1 + 4 + nSendDatLen + 1) * 2;
			byte[] bSendX30 = new byte[nSendLen];
			byte[] bCvtX30 = FpCoder.EncodeX30(bSend, 2, nSendDatLen + 6);
			System.arraycopy(bSend, 0, bSendX30, 0, 2);
			System.arraycopy(bCvtX30, 0, bSendX30, 2, bCvtX30.length);
			Logs.i(TAG, "CvtX30.length=" + bCvtX30.length);
			bSend = bSendX30;
			HexOut(TAG, bSend, 0, nSendLen);
		}
		HexOut(TAG, bSend, 0, nSendLen);

		// 发送命令请求
		nRet = doSendCmd(bSend, bSend.length);

		if (nCmdId == FpConst.LSR_CAPT) {
			return nRet;
		}

		Logs.i(TAG, "doSendCmd nRet=" + nRet);
		if (isWell(nRet)) {
			nRet = doRecvCmd();
			Logs.i(TAG, "doRecvCmd nRet=" + nRet);
		}
		Logs.i(TAG, "doCmdLaserX nRet=" + nRet);

		return nRet;
	}

	/*
	 * 执行 LaserX协议命令
	 * 
	 * 指令接口与外部交互均已Bin格式进行， 命令执行过程可以选用Split x30格式
	 */
	public int doCommand(int nCmdId) {
		return doCommand(nCmdId, nTimeoutDoComm);
	}

	/*
	 * 执行 LaserX协议命令
	 * 
	 * 指令接口与外部交互均已Bin格式进行， 命令执行过程可以选用Split x30格式
	 */
	public int doCommand(int nCmdId, int nTimeout) {
		int nRet = 0;
		sCurrHexCmdId = "0x"
				+ FpCoder.byte2String((byte) (nCmdId & 0x000000FF));
		Logs.i(TAG, "doCommand Start ");
		// 设定超时
		isCommTimeout(nTimeout);
		// 尝试清空缓冲
		readClear();
		switch (nCmdProtocol) {
		case CXY_LASERX:
			nRet = doCmdLaserX(nCmdId);
			break;
		default:
			nRet = FpConst.TCY_NSUP;
			break;
		}
		// 复位超时
		isCommTimeout(0);
		Logs.i(TAG, "doCommand End nRet = " + nRet);
		return nRet;
	}

	/**
	 * 将设备的响应码转换为API的错误码
	 * 
	 * @param nDevAck
	 * @return
	 */
	private int CnvtErrLaserX(int nDevAck) {
		switch (nDevAck) {
		case FpConst.LSE_FAIL:
			return (FpConst.TCY_FAIL); // 失败结果
		case FpConst.LSE_ERRO:
			return (FpConst.TCY_ERRO); // 校验错误
		case FpConst.LSE_TMOT:
			return (FpConst.TCY_TMOT); // 操作超时
		case FpConst.LSE_NODV:
			return (FpConst.TCY_NODV); // 未连设备
		case FpConst.LSE_FLSH:
			return (FpConst.TCY_FLSH); // 写闪存错
		case FpConst.LSE_NOFP:
			return (FpConst.TCY_NOFP); // 未按或孬
		case FpConst.LSE_NOSM:
			return (FpConst.TCY_NSAM); // 值不相关
		case FpConst.LSE_NOMT:
			return (FpConst.TCY_NMAT); // 值不匹配
		case FpConst.LSE_EPLB:
			return (FpConst.TCY_EMPT); // 库链为空
		case FpConst.LSE_TOLV:
			return (FpConst.TCY_TOLV); // 请抬起手
		case FpConst.LSE_NMEM:
			return (FpConst.TCY_NMEM); // 内存不足
		case FpConst.LSE_NSUP:
			return (FpConst.TCY_NSUP); // 不支持令
		case FpConst.LSE_LESS:
			return (FpConst.TCY_LESS); // 特点过少
		case FpConst.LSE_CNCL:
			return (FpConst.TCY_CNCL); // 取消操作
		case FpConst.LSE_BUSY:
			return (FpConst.TCY_BUSY); // 我很忙啊
		default: {
			if (nDevAck > 0)
				return -nDevAck;
			return nDevAck;
			// return (FpConst.TCY_NSUP);
		}
		}
	}

	private int openDevice(int nDevPort, int nDevBaudRate, int nCommProtocol) {
		return 0;
	}

	// Close device
	private void closeDevice() {
	}

	private void HexOut(String tag, byte[] bOut, int nOffSet, int nLen) {
		Logs.HexOut(tag, bOut, nOffSet, nLen);
	}

	/**
	 * read 方法必须确保可以读取到正确长度的数据，否则应该返回错误
	 */
	private int readClear() {
		int nRet = FpConst.TCY_NLNK;
		int nValen = 0;
		try {
			nValen = mInputStream.available();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
			return FpConst.TCY_FLSH;
		}
		if (nValen > 0) {
			try {
				byte[] bRdBuf = new byte[nValen];
				nRet = mInputStream.read(bRdBuf, 0, nValen);
				Logs.e("read Clear. nValen=" + nValen);
			} catch (IOException e) {
				e.printStackTrace();
				return FpConst.TCY_FLSH;
			}
		}
		return 0;
	}

	/**
	 * read 方法必须确保可以读取到正确长度的数据，否则应该返回错误
	 */
	@Override
	public int read(byte[] bReadOutBuf, int nStartPos, int nWantReadLen) {
		int nRet = FpConst.TCY_NLNK;
		int nRdLen = 0;
		int nValen = nWantReadLen;
		int nLeftLen = nWantReadLen - nRdLen;

		byte[] bRdBuf = new byte[nWantReadLen];
		// 接收请求头
		while (nRdLen < nWantReadLen) {

			// 取消
			if (nCancelFlag == 1) {
				nCancelFlag = 0;
				nRet = FpConst.TCY_CNCL;
				break;
			}
			// 通信超时
			if (isCommTimeout() == true) {
				nRet = FpConst.TCY_TMOT;
				break;
			}
			// 交易超时
			if (isFuncTimeout() == true) {
				nRet = FpConst.TCY_TMOT;
				break;
			}

			try {
				nValen = mInputStream.available();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
				return FpConst.TCY_FLSH;
			}

			if (nValen == 0) {
				try {
					Thread.sleep(10);
					// Logs.i(TAG,"Wait For Comm Data");
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					return FpConst.TCY_FLSH;
				}
			} else {
				nLeftLen = nWantReadLen - nRdLen;
				// 防止阻塞，只读取有效长度
				if (nValen < nLeftLen)
					nLeftLen = nValen;
				try {
					nRet = mInputStream.read(bRdBuf, nRdLen, nLeftLen);
					nRdLen += nRet;
				} catch (IOException e) {
					e.printStackTrace();
					return FpConst.TCY_FLSH;
				}
			}
		}
		// 根据读到的长度进行返回值修正,确保长度
		if (nRdLen > 0) {
			System.arraycopy(bRdBuf, 0, bReadOutBuf, nStartPos, nRdLen);
			if (nRdLen != nWantReadLen) {
				if (nRet >= 0) {
					return FpConst.TCY_NLNK;
				} else {
					return nRet;
				}
			}
			nRet = nRdLen;
		} else {
			if (nRet >= 0) {
				nRet = FpConst.TCY_NLNK;
			}
		}
		return nRet;
	}

	@Override
	public int write(byte[] data) {
		try {
			mOutputStream.write(data);
		} catch (IOException e) {
			e.printStackTrace();
			return FpConst.TCY_NLNK;
		}
		return FpConst.TCY_SUCC;
	}

	public int write(byte[] pbWriteBuf, int nStartPos, int nWantWriteLen) {
		if (isOpened() == false)
			return FpConst.TCY_NLNK;
		int nRet = 0;
		try {
			mOutputStream.write(pbWriteBuf, nStartPos, nWantWriteLen);
			nRet = nWantWriteLen;
		} catch (IOException e) {
			e.printStackTrace();
			nRet = FpConst.TCY_NLNK;
		}
		return nRet;
	}

	public int flush() {
		if (isOpened() == false)
			return FpConst.TCY_NLNK;
		try {
			mOutputStream.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return 0;
	}

	/**
	 * 获取设备info
	 * 
	 * @return
	 */
	public FingerResult getDevInfo() {
		isFuncTimeout(nTimeoutDoComm);
		FingerResult result = new FingerResult(FpConst.TCY_SUCC);
		int nRet;
		byte[] bRet = new byte[1024];
		String sRet = "";
		nRet = lxCmdDevLink(bRet);
		if (nRet >= 0) {
			int nM, nJ;
			System.arraycopy(getbRecvBuf(), 0, bRet, 0, getRecvDatLen());
			nM = (int) bRet[2];
			nJ = (int) bRet[3];
			Logs.i(TAG, "DevInfo:M=" + nM + ", J=" + nJ);
			sRet = "" + nM + "." + nJ;
			sRet += " W=" + (int) bRet[6] * 2 + " H=" + (int) bRet[7] * 2;
			result.setResult(sRet);
			Logs.i(TAG, "DevInfo:" + sRet);
		} else {
			Logs.i(TAG, "lxCmdDevLink=" + nRet);
			sRet = "" + nRet;
		}
		isFuncTimeout(0);
		result.setStatus(nRet);
		return result;
	}

	/**
	 * 获取设备sn
	 * 
	 * @return
	 */
	public FingerResult getDevSn() {
		isFuncTimeout(nTimeoutDoComm);
		FingerResult result = new FingerResult(FpConst.TCY_SUCC);
		int nRet;
		String sRet = "";
		nRet = lxCmdDito(0, 0, 0);
		Logs.i(TAG, "getDevSn" + nRet);
		if (nRet >= 0) {
			byte[] bSn = new byte[getRecvDatLen()];
			System.arraycopy(getbRecvBuf(), 0, bSn, 0, getRecvDatLen());
			sRet = new String(bSn);
			Logs.i(TAG, "sn length:" + bSn.length);
			result.setResult(sRet);
		}
		isFuncTimeout(0);
		result.setStatus(nRet);
		return result;
	}

	/**
	 * 变更设备波特率
	 * 
	 * @param nBaudRate
	 * @return
	 */
	public FingerResult tcBaudChange(int nBaudRate) {
		isFuncTimeout(nTimeoutDoComm);
		FingerResult result = new FingerResult(FpConst.TCY_SUCC);
		int nRet = lxCmdBaudChange(nBaudRate);

		Logs.i(TAG, "tcBaudChange" + nRet);
		isFuncTimeout(0);
		result.setStatus(nRet);
		return result;
	}

	/**
	 * 采集3枚指纹合成一枚模板
	 * 
	 * @param nTimeout
	 * @return
	 */
	public FingerResult tcRegisterInDev(int nTimeout) {
		isFuncTimeout(nTimeout);
		FingerResult result = new FingerResult();
		int nRet = FpConst.TCY_FAIL;
		// byte[] bFeat = new byte[2048];
		Logs.d(TAG, "tcRegisterInDev Enter.");

		while (!isFuncTimeout()) {
			nRet = lxCmdRegistInDev(0);
			Logs.d(TAG, "lxCmdRegistInDev Over. nRet=" + nRet);
			if (nRet < 0) {
				if (nRet == FpConst.TCY_NOFP) {
					continue;
				}
				Logs.d(TAG, "tcRegisterInDev:" + nRet);
				isFuncTimeout(nTimeout);
				return result.setStatus(nRet);
			} else {
				Logs.d(TAG, "tcRegisterInDev Len:" + nRet);
				byte[] bFeat = new byte[512];
				System.arraycopy(getbRecvBuf(), 0, bFeat, 0, getRecvDatLen());
				result.setTemplate(bFeat);
				break;
			}
		}
		result.setStatus(nRet);
		isFuncTimeout(0);
		return result;
	}

	/**
	 * 获取指纹特征
	 * 
	 * @param nTimeout
	 * @return
	 */
	public FingerResult tcGetFeature(int nTimeout) {
		isFuncTimeout(nTimeout);
		boolean isTimeout = false;
		FingerResult result = new FingerResult(0);
		int nRet = 0;
		byte[] bFeat = new byte[2048];
		Logs.d(TAG, "tcGetFeature Enter.");
		// 设置无指纹时的等待抓拍次数
		nRet = lxCmdDito(1, 8, 10);
		Logs.d(TAG, "lxCmdDito nRet:" + nRet);
		if (nRet < 0)
			return result.setStatus(nRet);
		while (!(isTimeout = isFuncTimeout())) {
			nRet = lxCmdVerifyInDev(bFeat);
			Logs.d(TAG, "lxCmdVerifyInDev Over. nRet=" + nRet);
			if (nRet < 0) {
				if (nRet == FpConst.TCY_NOFP) {
					continue;
				}
				isFuncTimeout(nTimeout);
				return result.setStatus(nRet);
			} else {
				Logs.i(TAG, "tcGetFeature Len:" + nRet);
				byte[] bRet = new byte[512];
				System.arraycopy(bFeat, 0, bRet, 0, nRet);
//				String sFeat = Base64.encodeToString(bRet, Base64.NO_WRAP);
//				result.setResult(sFeat);
				result.setTemplate(bRet);
//				Logs.i(TAG, "tcGetFeature:" + sFeat);
				break;
			}
		}
		if (isTimeout) {
			return result.setStatus(FpConst.TCY_TMOT);
		}
		result.setStatus(nRet);
		Logs.d(TAG, "背光蜂鸣");
		lxCmdSubDev(0, 10);
		isFuncTimeout(0);
		isCommTimeout(0);
		return result;
	}

	/**
	 * 获取指纹图像
	 * 
	 * @return
	 */
	public FingerResult tcGetImg(int nTimeout) {
		isFuncTimeout(nTimeout);
		FingerResult result = new FingerResult();
		int nRet = 0;
		Logs.d(TAG, "tcGetImg Enter.");
		// 设置无指纹时的等待抓拍次数
		nRet = lxCmdDito(1, 8, 10);
		Logs.d(TAG, "lxCmdDito nRet:" + nRet);
		if (nRet < 0)
			return result.setStatus(nRet);

		while (!isFuncTimeout()) {
			nRet = lxCmdCapture(0);
			Logs.d(TAG, "tcGetImg Over. nRet=" + nRet);
			if (nRet < 0) {
				if (nRet == FpConst.TCY_NOFP) {
					continue;
				}

				isFuncTimeout(nTimeout);
				return result.setStatus(nRet);
			} else {
				Logs.i(TAG,"image size:" + mImgByte.length);
				FpImage fpImage = new FpImage();
				fpImage.saveRawAsBmpBuf(mImgByte, mWidth, mHeight);
				result.setImgBytes(fpImage.getBmpImgBuf());
				break;
			}
		}

		result.setStatus(nRet);
		Logs.d(TAG, "背光蜂鸣");
		lxCmdSubDev(0, 10);
		isFuncTimeout(0);
		return result;
	}
	
	public void setCancel() {
		nCancelFlag = 1;
	}

	public boolean isOpened() {
		if (mInputStream != null && mOutputStream != null)
			return true;
		return false;
	}

	public boolean isConnected() {
		if (mInputStream == null || mOutputStream == null)
			return false;

		isFuncTimeout(nTimeoutDoComm);
		return lxCmdDevLink(new byte[1024]) >= 0;
	}

	@Override
	public boolean isCancel() {
		return nCancelFlag == 1;
	}
}
